package static_hybrid;

import java.io.*;
import java.util.Scanner;

public class Matrix {

	public static int[][] multiply(int[][] m1, int[][] m2) {
		int m1rows = m1.length;
		int m1cols = m1[0].length;
		int m2rows = m2.length;
		int m2cols = m2[0].length;
		if (m1cols != m2rows)
			throw new IllegalArgumentException("matrices don't match: "
					+ m1cols + " != " + m2rows);
		int[][] result = new int[m1rows][m2cols];

		// multiply
		for (int i = 0; i < m1rows; i++)
			for (int j = 0; j < m2cols; j++)
				for (int k = 0; k < m1cols; k++)
					result[i][j] = (result[i][j] + (m1[i][k] * m2[k][j])) % 2;

		return result;
	}

	public static void printMatrix(int[][] a, String message) {
		int rows = a.length;
		int cols = a[0].length;
		System.out.println(message);
		for (int i = 0; i < rows; i++) {
			System.out.print("");
			for (int j = 0; j < cols; j++)
				System.out.format("%4d", a[i][j]);
			System.out.println(" ");
		}
		System.out.println(":;");
	}

	public static int[][] blockArray(int alfa, int beta, int teta) {
		int ROW = 5, COLUMN = 5;

		int[][] result = new int[ROW][COLUMN];

		for (int i = 0; i < ROW; i++)
			for (int j = 0; j < COLUMN; j++) {
				result[i][i] = alfa;
				result[i][(i + 1) % ROW] = beta;
				result[i][(i + 4) % ROW] = teta;
			}

		return result;

	}

	public static int[][] defineTransitionMatrix(int alpha, int beta, int phei,
			int gamma, int delta, int psei, int theta, int omega, int mu) {
		int ROW = 25, COLUMN = 25;
		int[][] result = new int[ROW][COLUMN];
		int[][] alphaMatrix = Matrix.blockArray(alpha, beta, phei);
		int[][] gammaMatrix = Matrix.blockArray(gamma, delta, psei);
		int[][] thetaMatrix = Matrix.blockArray(theta, omega, mu);

		for (int i = 0, m = 0; i < ROW; i += 5, m++) {
			for (int k = 0; k < 5; k++) {
				for (int j = i; j < i + 5; j++) {
					result[i + k][j] = alphaMatrix[k % 5][j % 5];
					result[i + k][(j + 5) % 25] = gammaMatrix[k % 5][j % 5];
					result[i + k][(j + 20) % 25] = thetaMatrix[k % 5][j % 5];
				}
			}
		}

		return result;

	}

	public static int[] blockValues(int number) {
		int[] result = new int[9];

		String binary = Integer.toBinaryString(number);
		int binaryLength = binary.length();

		System.out.println(binary);
		for (int i = 0; i < binaryLength; i++)
			result[i] = Integer.parseInt(Character.toString(binary
					.charAt(binaryLength - (i + 1))));

		return result;

	}

	public static int[][] formRuleMatrix(int rulenumber) {
		int ROW = 25, COLUMN = 25;
		int[][] result = new int[ROW][COLUMN];
		int alpha, beta, phei, gamma, delta, psei, theta, omega, mu;
		int[] blockvalues = Matrix.blockValues(rulenumber);
		alpha = blockvalues[0];
		beta = blockvalues[1];
		delta = blockvalues[2];
		gamma = blockvalues[3];
		psei = blockvalues[4];
		phei = blockvalues[5];
		mu = blockvalues[6];
		theta = blockvalues[7];
		omega = blockvalues[8];

		result = Matrix.defineTransitionMatrix(alpha, beta, phei, gamma, delta,
				psei, theta, omega, mu);
		System.out.println("Block Values for " + rulenumber + "; alpha:"
				+ alpha + " ,beta :" + beta + " ,phei:" + phei + " ,gamma:"
				+ gamma + " ,delta:" + delta + " ,psei:" + psei + " ,theta:"
				+ theta + " ,omega:" + omega + " ,mu:" + mu);
		return result;

	}

	public static int[][] hybridMatrix(int RULE1[][], int RULE2[][]) {
		int ROW = 25, COLUMN = 25;
		int[][] result = new int[ROW][COLUMN];

		for (int i = 0; i < ROW; i++) {
			if (i % 2 == 0) {
				System.arraycopy(RULE1[i], 0, result[i], 0, COLUMN);
			}

			if (i % 2 == 1) {
				System.arraycopy(RULE2[i], 0, result[i], 0, 25);
			}
		}
		return result;
	}

	public static int[][] getInitialColumnVector() {
		int ROW = 25, COLUMN = 1;
		int[][] result = new int[ROW][COLUMN];
		Scanner scanner = new Scanner(System.in);
		System.out.println("Enter Initial Column Vector Values Row wise:");
		for (int i = 0; i < ROW; i++)
			result[i][0] = scanner.nextInt();

		return result;

	}

	public static int[][] getVector(int RULE1VALUE, int RULE2VALUE,
			int[][] INITIALCOLUMNVECTOR) {
		int VECTORLENGTH = 1024;
		int[][] result = new int[1][VECTORLENGTH];
		int[][] RULE1, RULE2, HYBRID, TMPRESULT;
		RULE1 = Matrix.formRuleMatrix(RULE1VALUE);
		RULE2 = Matrix.formRuleMatrix(RULE2VALUE);
		HYBRID = Matrix.hybridMatrix(RULE1, RULE2);

		Matrix.printMatrix(RULE1, "Rule Matrix for " + RULE1VALUE);
		Matrix.printMatrix(RULE2, "Rule Matrix for " + RULE2VALUE);
		Matrix.printMatrix(HYBRID, "HYBRID MATRIX ");
		TMPRESULT = INITIALCOLUMNVECTOR;
		for (int i = 0; i < VECTORLENGTH; i++) {
			TMPRESULT = Matrix.multiply(HYBRID, TMPRESULT);
			result[0][i] = TMPRESULT[13][0];
		}

		return result;

	}

	public static void statistics(int[][] RESULT, FileWriter fstream) {
		int ROW = RESULT.length;
		int COLUMN = RESULT[0].length;
		String RESULTVECTOR = "";
		try {

			BufferedWriter yaz = new BufferedWriter(fstream);
			yaz.write("THE STATISTICAL RESULTS FOR THE VECTOR");
			yaz.newLine();
			yaz.newLine();
			for (int k = 0; k < COLUMN; k++) {
				yaz.write(RESULT[0][k] + " ");
				RESULTVECTOR += RESULT[0][k];
			}
			byte[] a = RESULTVECTOR.getBytes();
			System.out.println(a[1] ^ a[2]);
			yaz.newLine();
			yaz.newLine();

			int[] result1 = Matrix.getResult(1, RESULT);
			int[] result2 = Matrix.getResult(2, RESULT);
			int[] result3 = Matrix.getResult(3, RESULT);
			int[] result4 = Matrix.getResult(4, RESULT);

			yaz.newLine();
			Matrix.writeResults(1, result1, yaz);
			yaz.newLine();
			Matrix.frequencyTest(result1, COLUMN, yaz);
			yaz.newLine();
			Matrix.writeResults(2, result2, yaz);
			yaz.newLine();

			Matrix.serialTest(result2, result1, COLUMN, yaz);
			yaz.newLine();
			Matrix.writeResults(3, result3, yaz);
			yaz.newLine();
			Matrix.pokerTest(result3, COLUMN, yaz, 3);
			yaz.newLine();
			Matrix.writeResults(4, result4, yaz);
			yaz.newLine();
			Matrix.runsTest(RESULT, COLUMN, yaz);
			yaz.newLine();
			Matrix.autocorrelationTest(RESULTVECTOR, 8, yaz); // 8 kaydırma
																// miktarı
			yaz.write(RESULTVECTOR);
			yaz.close();
		} catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
	}

	public static int[] getResult(int number, int[][] vector) {
		int RESULTVECTORLENGTH = vector[0].length;
		int LENGTH = (int) Math.pow(2, number);
		int[] result = new int[LENGTH];
		for (int j = 0; j < RESULTVECTORLENGTH - (number - 1);) {
			String dizi = "";
			for (int i = 0; i < number; i++) {
				dizi += Integer.toString(vector[0][j + i]);
			}
			int index = Integer.parseInt(dizi, 2);
			result[index]++;

			if (number == 2)
				j++;
			else
				j = j + number;

		}
		return result;
	}

	public static void writeResults(int value, int[] result, BufferedWriter yaz)
			throws IOException {
		yaz.write(value + " 'li(lü) şekilde sıralanmış dizilerin sayısı");
		yaz.newLine();

		int LENGTH = result.length;
		for (int i = 0; i < LENGTH; i++) {
			String binary = Integer.toBinaryString(i);
			int blength = binary.length();
			for (int j = 0; j < (value - blength); j++)
				binary = "0" + binary;
			yaz.write(binary + " dizisi : " + result[i]);
			yaz.newLine();
		}
	}

	public static double frequencyTest(int[] testarray, int bitnumber,
			BufferedWriter yaz) throws IOException {
		int numberofzeros = testarray[0];
		int numberofones = testarray[1];
		double result = Math.pow((numberofzeros - numberofones), 2) / bitnumber;
		yaz.newLine();
		yaz.write("FREQUENCY TEST RESULT : " + result);
		yaz.newLine();
		yaz.newLine();
		yaz.newLine();
		return result;
	}

	public static double serialTest(int[] testarray1, int[] testarray2,
			int bitnumber, BufferedWriter yaz) throws IOException {
		int zerozero = (int) Math.pow(testarray1[0], 2);
		int zeroone = (int) Math.pow(testarray1[1], 2);
		int onezero = (int) Math.pow(testarray1[2], 2);
		int oneone = (int) Math.pow(testarray1[3], 2);

		int zerosquare = (int) Math.pow(testarray2[0], 2);
		int onesquare = (int) Math.pow(testarray2[1], 2);

		float total1 = (float) (zerozero + zeroone + onezero + oneone);
		float total2 = (float) (zerosquare + onesquare);
		float fourdivide = (float) (4.0 / (((float) bitnumber) - 1.0));
		float twodivide = (float) (2.0 / ((float) bitnumber));

		double result = 1 + fourdivide * (total1) - (twodivide * (total2));
		yaz.newLine();
		yaz.write("SERIAL TEST RESULT : " + result);
		yaz.newLine();
		yaz.newLine();
		yaz.newLine();

		return result;
	}

	public static double pokerTest(int[] testarray, int bitnumber,
			BufferedWriter yaz, int testvalue) throws IOException {

		int m = (int) Math.pow(2, testvalue);
		int k = bitnumber / testvalue;
		float prefix = ((float) m) / ((float) k);
		int total = 0;
		for (int i = 0; i < testarray.length; i++) {
			total += (int) Math.pow(testarray[i], 2);
		}

		double result = prefix * total - k;
		yaz.newLine();
		yaz.write("POKER TEST RESULT : " + result);
		yaz.newLine();
		yaz.newLine();
		yaz.newLine();

		return result;
	}

	public static double runsTest(int[][] RESULT, int bitnumber,
			BufferedWriter yaz) throws IOException {
		int K = 5;
		float total1 = (float) 0;
		float total2 = (float) 0;
		int[] blocks = Matrix.numberOfBlocks(RESULT);
		int[] gaps = Matrix.numberOfGaps(RESULT);
		for (int i = 1; i <= K; i++) {
			float ei = (float) ((float) (bitnumber - i + 3))
					/ (float) Math.pow(2, i + 2);
			float pay = ((float) blocks[i]) - ei;
			float blockresult = (float) Math.pow(pay, 2) / ei;

			pay = ((float) gaps[i]) - ei;
			float gapresult = (float) Math.pow(pay, 2) / ei;

			total1 += blockresult;
			total2 += gapresult;

		}

		double result = total1 + total2;
		yaz.newLine();
		yaz.write("RUNS TEST RESULT : " + result);
		yaz.newLine();
		yaz.newLine();
		yaz.newLine();
		return result;
	}

	public static double autocorrelationTest(String RESULTVECTOR, int dvalue,
			BufferedWriter yaz) throws IOException {
		String secondstring = "";
		int total = 0;
		int LENGTH = RESULTVECTOR.length();

		for (int i = dvalue; i < RESULTVECTOR.length(); i++) {
			secondstring += RESULTVECTOR.charAt(i);
		}
		byte[] second = secondstring.getBytes();
		byte[] original = RESULTVECTOR.getBytes();

		for (int i = 0; i < secondstring.length(); i++) {
			total += second[i] ^ original[i];
		}

		float parent = (LENGTH - dvalue) / 2;
		float sqr = (float) Math.sqrt(LENGTH - dvalue);
		float result = 2 * ((total - parent) / sqr);
		yaz.newLine();
		yaz.write("AUTOCORRELATION TEST RESULT : " + result);
		yaz.newLine();
		yaz.newLine();
		yaz.newLine();
		return result;
	}

	public static int[] numberOfBlocks(int[][] RESULT) {
		int LENGTH = RESULT[0].length;
		int[] result = new int[LENGTH];
		for (int i = 0; i < LENGTH;) {
			int count = 0;
			while (i < LENGTH && RESULT[0][i] == 1) {
				count++;
				i++;
			}
			result[count]++;
			i++;
		}
		return result;
	}

	public static int[] numberOfGaps(int[][] RESULT) {
		int LENGTH = RESULT[0].length;
		int[] result = new int[LENGTH];
		for (int i = 0; i < LENGTH;) {
			int count = 0;
			while (i < LENGTH && RESULT[0][i] == 0) {
				count++;
				i++;
			}
			result[count]++;
			i++;
		}
		return result;
	}

	public static void main(String[] argv) {

		try {
			FileWriter fstream = new FileWriter("out.txt");

			int[][] MATRIXRESULT;
			int[][] INITIALCOLUMNVECTOR = { { 1 }, { 0 }, { 1 }, { 0 }, { 1 },
					{ 1 }, { 1 }, { 0 }, { 1 }, { 1 }, { 0 }, { 1 }, { 1 },
					{ 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 1 },
					{ 1 }, { 1 }, { 1 }, { 1 } };

			int RULE1VALUE = 109, RULE2VALUE = 308;
			// Matrix.defineTransitionMatrix(alpha, beta, phei, gamma, delta,
			// psei, theta, omega, mu)
			INITIALCOLUMNVECTOR = Matrix.getInitialColumnVector();
			// Matrix.printMatrix(INITIALCOLUMNVECTOR, "Initial Column Vector");
			MATRIXRESULT = Matrix.getVector(RULE1VALUE, RULE2VALUE,
					INITIALCOLUMNVECTOR);
			Matrix.printMatrix(MATRIXRESULT, "RESULT VECTOR");
			Matrix.statistics(MATRIXRESULT, fstream);
			// int[][] tmp
			// ={{1,1,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1}}
			// ;
			// Matrix.statistics(tmp, fstream);
		} catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
	}
}
